/**
 * ExceptionMatcherBuilder.java
 * 
 * Created on Jul 4, 2008 at 10:25:02 PM
 */
package org.lego4j.matchers;

import java.util.concurrent.Callable;

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
import org.hamcrest.TypeSafeMatcher;

/**
 * TODO add javadoc to ExceptionMatcherBuilder
 *
 * @author <a href="alexguev@gmail.com">Alexei Guevara</>
 * @version $Revision:$ updated on $Date:$ by $Author:$
 */
public class ExceptionMatcherBuilder
    extends TypeSafeMatcher<Callable<?>>
    implements ThrowsExceptionSyntax, ExceptionMessageSyntax, ExceptionCauseSyntax {
    
    private Matcher<?> myThrownExceptionMatcher = Matchers.any( Exception.class );
    private Matcher<String> myThrownExceptionMessageMatcher = Matchers.any( String.class );
    private Matcher<?> myThrownExceptionCauseMatcher = Matchers.anything();

    public ExceptionMatcherBuilder() {
    }
    
    @Override
    public boolean matchesSafely( Callable<?> theCallable ) {
        try {
            theCallable.call();
        }
        catch ( Exception e ) {
            if (myThrownExceptionMatcher.matches( e ) 
             && myThrownExceptionMessageMatcher.matches( e.getMessage() ) 
             && myThrownExceptionCauseMatcher.matches( e.getCause() ) ) {
                return true;
            }
        }
        
        return false;
    }

    public void describeTo(Description theDescription) {
        theDescription.appendText( "Must throw an exception that matches: " );
        theDescription.appendDescriptionOf( myThrownExceptionMatcher );
        theDescription.appendText( "whose message matches: " );
        theDescription.appendDescriptionOf( myThrownExceptionMessageMatcher );
        theDescription.appendText( "and whose cause matches: " );
        theDescription.appendDescriptionOf( myThrownExceptionCauseMatcher );
    }

    public <T extends Exception> ExceptionMessageSyntax throwsException( Matcher<T> theThrownExceptionMatcher) {
        myThrownExceptionMatcher = theThrownExceptionMatcher;
        
        return this;
    }

    public <T extends Exception> ExceptionMessageSyntax throwsException() {
        return this;
    }

    public ExceptionCauseSyntax withMessage(Matcher<String> theMessageMatcher) {
        myThrownExceptionMessageMatcher = theMessageMatcher;
        
        return this;
    }

    public <T extends Throwable> Matcher<Callable<?>> withCause( Matcher<T> theCauseMatcher) {
        myThrownExceptionCauseMatcher = theCauseMatcher;
        
        return this;
    }

}
